home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / opt / pinstaller / GLIStorageDevice.py < prev    next >
Text File  |  2006-05-26  |  28KB  |  795 lines

  1. # Copyright 1999-2005 Gentoo Foundation
  2. # This source code is distributed under the terms of version 2 of the GNU
  3. # General Public License as published by the Free Software Foundation, a copy
  4. # of which can be found in the main directory of this project.
  5.  
  6. import commands, string, os, parted
  7. from glob import glob
  8. from GLIException import *
  9. import GLIUtility
  10.  
  11. MEGABYTE = 1024 * 1024
  12.  
  13. # these are here so that we can change them easily in future
  14. # the values were chosen to represent perfect floating point representations
  15. FREE_MINOR_FRAC_PRI = 1.0/32.0
  16. FREE_MINOR_FRAC_LOG = 1.0/8.0
  17.  
  18. archinfo = { 'sparc': { 'fixedparts': [ { 'minor': 3, 'type': "wholedisk" } ], 'disklabel': 'sun', 'extended': False },
  19.              'hppa': { 'fixedparts': [ { 'minor': 1, 'type': "palo" } ], 'disklabel': 'msdos', 'extended': True },
  20.              'x86': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True },
  21.              'amd64': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True },
  22.              'ppc': { 'fixedparts': [ { 'minor': 1, 'type': "metadata" } ], 'disklabel': 'mac', 'extended': False }
  23.            }
  24.  
  25. ##
  26. # This class provides a partitioning abstraction for the frontends
  27. class Device:
  28.     "Class representing a partitionable device."
  29.     
  30.     _device = None
  31.     _partitions = None
  32.     _geometry = None
  33.     _total_bytes = 0
  34.     _total_sectors = 0
  35.     _cylinder_bytes = 0
  36.     _sectors_in_cylinder = 0
  37.     _parted_dev = None
  38.     _parted_disk = None
  39.     _sector_bytes = 0
  40.     _total_mb = 0
  41.     _arch = None
  42.     _disklabel = None
  43.  
  44.     ##
  45.     # Initialization function for GLIStorageDevice class
  46.     # @param device Device node (e.g. /dev/hda) of device being represented
  47.     # @param arch="x86" Architecture that we're partition for (defaults to 'x86' for now)
  48.     def __init__(self, device, arch="x86", set_geometry=True, local_device=True):
  49.         self._device = device
  50.         self._partitions = {}
  51.         self._geometry = {'cylinders': 0, 'heads': 0, 'sectors': 0, 'sectorsize': 512}
  52.         self._total_bytes = 0
  53.         self._cylinder_bytes = 0
  54.         self._arch = arch
  55.         self._local_device = local_device
  56.         if self._local_device:
  57.             self._parted_dev = parted.PedDevice.get(self._device)
  58.             try:
  59.                 self._parted_disk = parted.PedDisk.new(self._parted_dev)
  60.             except:
  61.                 self._parted_disk = self._parted_dev.disk_new_fresh(parted.disk_type_get(archinfo[self._arch]['disklabel']))
  62.             self._disklabel = self._parted_disk.type.name
  63.         else:
  64.             self._disklabel = archinfo[self._arch]['disklabel']
  65.         if set_geometry:
  66.             self.set_disk_geometry_from_disk()
  67.  
  68.     ##
  69.     # Sets disk geometry info from disk. This function is used internally by __init__()
  70.     def set_disk_geometry_from_disk(self):
  71.         self._total_bytes = self._parted_dev.length * self._parted_dev.sector_size
  72.         self._geometry['heads'], self._geometry['sectors'], self._geometry['cylinders'] = self._parted_dev.heads, self._parted_dev.sectors, self._parted_dev.cylinders
  73.         self._sector_bytes = self._parted_dev.sector_size
  74.         self._cylinder_bytes = self._geometry['heads'] * self._geometry['sectors'] * self._sector_bytes
  75.         self._total_sectors = self._parted_dev.length
  76.         self._sectors_in_cylinder = self._geometry['heads'] * self._geometry['sectors']
  77.         self._total_mb = long(self._total_bytes / MEGABYTE)
  78.  
  79.     ##
  80.     # Sets partition info from disk.
  81.     def set_partitions_from_disk(self):
  82.         last_part = 0
  83.         last_log_part = 4
  84.         parted_part = self._parted_disk.next_partition()
  85.         while parted_part:
  86.             part_mb = long((parted_part.geom.end - parted_part.geom.start + 1) * self._sector_bytes / MEGABYTE)
  87.             # Ignore metadata "partitions"...this will need to be changed for non-x86
  88.             if parted_part.num >= 1:
  89.                 fs_type = ""
  90.                 if parted_part.fs_type != None: fs_type = parted_part.fs_type.name
  91.                 if parted_part.type == 2: fs_type = "extended"
  92.                 if archinfo[self._arch]['extended'] and parted_part.num > 4:
  93.                     last_log_part = parted_part.num
  94.                 else:
  95.                     last_part = parted_part.num
  96.                 self._partitions[int(parted_part.num)] = Partition(self, parted_part.num, part_mb, parted_part.geom.start, parted_part.geom.end, fs_type, format=False, existing=True)
  97.             elif parted_part.type_name == "free":
  98.                 if self.get_extended_partition() and parted_part.geom.start >= self._partitions[self.get_extended_partition()].get_start() and parted_part.geom.end <= self._partitions[self.get_extended_partition()].get_end():
  99.                     self._partitions[last_log_part+FREE_MINOR_FRAC_LOG] = Partition(self, last_log_part+FREE_MINOR_FRAC_LOG, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
  100.                     last_log_part += 1
  101.                 else:
  102.                     self._partitions[last_part+FREE_MINOR_FRAC_PRI] = Partition(self, last_part+FREE_MINOR_FRAC_PRI, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
  103.                     last_part += 1
  104.             parted_part = self._parted_disk.next_partition(parted_part)
  105.  
  106.     ##
  107.     # Imports partition info from the install profile partition structure
  108.     # @param ips Parameter structure returned from install_profile.get_partition_tables()
  109.     def set_partitions_from_install_profile_structure(self, ips):
  110.         for part in ips:
  111.             tmppart = ips[part]
  112.             existing = False
  113.             if tmppart['origminor'] and not tmppart['format']:
  114.                 existing = True
  115.             self._partitions[tmppart['minor']] = Partition(self, tmppart['minor'], tmppart['mb'], tmppart['start'], tmppart['end'], tmppart['type'], format=tmppart['format'], origminor=tmppart['origminor'], existing=existing, mountpoint=tmppart['mountpoint'], mountopts=tmppart['mountopts'], mkfsopts=tmppart['mkfsopts'], resized=(existing and tmppart['resized']))
  116.  
  117.     ##
  118.     # Returns name of device (e.g. /dev/hda) being represented
  119.     def get_device(self):
  120.         return self._device
  121.  
  122.     ##
  123.     # Returns whether the device is local or not
  124.     def is_local(self):
  125.         return self._local_device
  126.  
  127.     ##
  128.     # Uses magic to apply the recommended partition layout
  129.     def do_recommended(self):
  130.         free_minor = 0
  131.         recommended_parts = [ { 'type': "ext2", 'size': 100, 'mountpoint': "/boot" },
  132.                               { 'type': "linux-swap", 'size': 512, 'mountpoint': "" },
  133.                               { 'type': "ext3", 'size': "*", 'mountpoint': "/" } ]
  134.         to_create = []
  135.         physical_memory = int(GLIUtility.spawn(r"free -m | egrep '^Mem:' | sed -e 's/^Mem: \+//' -e 's/ \+.\+$//'", return_output=True)[1].strip())
  136.         parts = self.get_ordered_partition_list()
  137.         # Search for concurrent unallocated space >=4GB
  138.         for part in parts:
  139.             if self._partitions[part].get_type() == "free" and self._partitions[part].get_mb() >= 4096:
  140.                 free_minor = part
  141.                 break
  142.         if not free_minor:
  143.             raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "You do not have atleast 4GB of concurrent unallocated space. Please remove some partitions and try again.")
  144.         remaining_free = self._partitions[free_minor].get_mb()
  145.         for newpart in recommended_parts:
  146.             # extended/logical partitions suck like a hoover
  147.             if archinfo[self._arch]['extended'] and free_minor == (3 + FREE_MINOR_FRAC_PRI) and not newpart == recommended_parts[-1]:
  148.                 if self.get_extended_partition():
  149.                     raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "This code is not yet robust enough to handle automatic partitioning with your current layout.")
  150.                 to_create.append({ 'type': "extended", 'size': remaining_free, 'mountpoint': "", 'free_minor': free_minor })
  151.                 free_minor = 4 + FREE_MINOR_FRAC_LOG
  152.             newpart['free_minor'] = free_minor
  153.             # Small hack to calculate optimal swap partition size
  154.             if newpart['type'] == "linux-swap" and physical_memory and physical_memory < 1024:
  155.                 newpart['size'] = physical_memory * 2
  156.             to_create.append(newpart)
  157.             free_minor = free_minor + 1
  158.             if not newpart['size'] == "*":
  159.                 remaining_free = remaining_free - newpart['size']
  160.         for newpart in to_create:
  161.             if newpart['size'] == "*":
  162.                 # This doesn't seem quite right...it should probably be set to remaining_free
  163.                 newpart['size'] = self._partitions[newpart['free_minor']].get_mb()
  164.             self.add_partition(newpart['free_minor'], newpart['size'], 0, 0, newpart['type'], mountpoint=newpart['mountpoint'])
  165.  
  166.     ##
  167.     # Combines free space and closes gaps in minor numbers. This is used internally
  168.     def tidy_partitions(self):
  169.         last_minor = 0
  170.         last_log_minor = 4
  171.         last_free = 0
  172.         last_log_free = 0
  173.         parts = self._partitions.keys()
  174.         parts.sort()
  175.         for part in parts:
  176.             if archinfo[self._arch]['extended'] and part > (4 + FREE_MINOR_FRAC_PRI): break
  177.             tmppart = self._partitions[part]
  178.             if tmppart.get_type() == "extended":
  179.                 for part_log in parts:
  180.                     if part_log < (4 + FREE_MINOR_FRAC_LOG): continue
  181.                     tmppart_log = self._partitions[part_log]
  182.                     if tmppart_log.get_type() == "free":
  183.                         if last_log_minor < last_log_free:
  184.                             self._partitions[last_log_free].set_mb(self._partitions[last_log_free].get_mb()+tmppart_log.get_mb())
  185.                             del self._partitions[part_log]
  186.                         else:
  187.                             if not last_log_free:
  188.                                 last_log_free = last_log_minor + FREE_MINOR_FRAC_LOG
  189.                             else:
  190.                                 last_log_free = part_log
  191.                             tmppart_log.set_minor(last_log_free)
  192.                             self._partitions[last_log_free] = tmppart_log
  193.                             if part_log != last_log_free: del self._partitions[part_log]
  194.                     else:
  195.                         if part_log > (last_log_minor + 1):
  196.                             tmppart_log.set_minor(last_log_minor + 1)
  197.                             last_log_minor = last_log_minor + 1
  198.                             self._partitions[last_log_minor] = tmppart_log
  199.                             del self._partitions[part_log]
  200.                         else:
  201.                             last_log_minor = part_log
  202.             elif tmppart.get_type() == "free":
  203.                 if last_minor < last_free:
  204.                     self._partitions[last_free].set_mb(self._partitions[last_free].get_mb()+tmppart.get_mb())
  205.                     del self._partitions[part]
  206.                 else:
  207.                     if not last_free:
  208.                         last_free = last_minor + FREE_MINOR_FRAC_PRI
  209.                     else:
  210.                         last_free = part
  211.                     tmppart.set_minor(last_free)
  212.                     self._partitions[last_free] = tmppart
  213.                     if part != last_free: del self._partitions[part]
  214.             else:
  215.                 if part > (last_minor + 1):
  216.                     tmppart.set_minor(last_minor + 1)
  217.                     last_minor = last_minor + 1
  218.                     self._partitions[last_minor] = tmppart
  219.                     del self._partitions[part]
  220.                 else:
  221.                     last_minor = part
  222.  
  223.     ##
  224.     # Adds a new partition to the partition info
  225.     # @param free_minor minor of unallocated space partition is being created in
  226.     # @param mb size of partition in MB
  227.     # @param start Start sector (only used for existing partitions)
  228.     # @param end End sector (only used for existing partitions)
  229.     # @param type Partition type (ext2, ext3, fat32, linux-swap, free, extended, etc.)
  230.     # @param mountpoint='' Partition mountpoint
  231.     # @param mountopts='' Partition mount options
  232.     # @param mkfsopts='' Additional mkfs options
  233.     def add_partition(self, free_minor, mb, start, end, type, mountpoint='', mountopts='',mkfsopts=''):
  234.         # Automatically pick the first unused minor if not a local device
  235.         if not self._local_device or free_minor == -1:
  236.             tmpparts = self._partitions.keys()
  237.             tmpparts.sort()
  238.             tmpminor = 0
  239.             if len(tmpparts):
  240.                 tmpminor = tmpparts[-1]
  241.             if archinfo[self._arch]['extended'] and tmpminor >= 5:
  242.                 free_minor = tmpminor + FREE_MINOR_FRAC_LOG
  243.             else:
  244.                 free_minor = tmpminor + FREE_MINOR_FRAC_PRI
  245.             self._partitions[free_minor] = Partition(self, free_minor, mb, 0, 0, "free")
  246.         new_minor = int(free_minor) + 1
  247.         if self._local_device:
  248.             # Check to see if the new minor we picked already exists. If it does, scoot all partitions from
  249.             # that one on down a minor
  250.             if self._partitions.has_key(new_minor):
  251.                 parts = self._partitions.keys()
  252.                 parts.sort()
  253.                 parts.reverse()
  254.                 hole_at = 0
  255.                 for i in range(1, parts[0]+1):
  256.                     if i <= new_minor: continue
  257.                     if not self._partitions.has_key(i):
  258.                         hole_at = i
  259.                         break
  260.                 stopscooting = 0
  261.                 for i in parts:
  262.                     if stopscooting: break
  263.                     if i >= hole_at and hole_at: continue
  264.                     if i >= new_minor:
  265.                         self._partitions[i].set_minor(i+1)
  266.                         self._partitions[i+1] = self._partitions[i]
  267.                         if i == new_minor: stopscooting = 1
  268.             # If the size specified for the new partition is less than the size of the unallocated space that it
  269.             # is getting placed into, a new partition to represent the remaining unallocated space needs to be
  270.             # created.
  271.             if mb != self._partitions[free_minor].get_mb():
  272.                 old_free_mb = self._partitions[free_minor].get_mb()
  273.                 del self._partitions[free_minor]
  274.                 if archinfo[self._arch]['extended'] and new_minor >= 5:
  275.                     free_minor = new_minor + FREE_MINOR_FRAC_LOG
  276.                 else:
  277.                     free_minor = new_minor + FREE_MINOR_FRAC_PRI
  278.                 self._partitions[free_minor] = Partition(self, free_minor, old_free_mb-mb, 0, 0, "free")
  279.             else:
  280.                 del self._partitions[free_minor]
  281.         self._partitions[new_minor] = Partition(self, new_minor, mb, start, end, type, mountpoint=mountpoint, mountopts=mountopts,mkfsopts=mkfsopts)
  282.         # When we create an extended partition, we have to create the partition to represent the unallocated
  283.         # space inside of the extended partition
  284.         if type == "extended":
  285.             self._partitions[4 + FREE_MINOR_FRAC_LOG] = Partition(self, (4 + FREE_MINOR_FRAC_LOG), mb, 0, 0, "free")
  286.         self.tidy_partitions()
  287.         return new_minor
  288.  
  289.     ##
  290.     # Removes partition from partition info
  291.     # @param minor Minor of partition to remove
  292.     def remove_partition(self, minor):
  293.         tmppart = self._partitions[int(minor)]
  294.         free_minor = 0
  295.         if tmppart.is_logical():
  296.             free_minor = int(minor-1)+FREE_MINOR_FRAC_LOG
  297.         else:
  298.             free_minor = int(minor-1)+FREE_MINOR_FRAC_PRI
  299.         if free_minor in self._partitions:
  300.             self._partitions[free_minor].set_mb(self._partitions[free_minor].get_mb() + tmppart.get_mb())
  301.         else:
  302.             self._partitions[free_minor] = Partition(self, free_minor, tmppart.get_mb(), 0, 0, "free", format=False, existing=False)
  303.         del self._partitions[int(minor)]
  304.         self.tidy_partitions()
  305.  
  306.     ##
  307.     # This function clears the partition table
  308.     def clear_partitions(self):
  309.         self._partitions = { (0 + FREE_MINOR_FRAC_PRI): Partition(self, (0 + FREE_MINOR_FRAC_PRI), self.get_total_mb(), 0, 0, "free", format=False, existing=False) }
  310.  
  311.     ##
  312.     # Returns an ordered list (disk order) of partition minors
  313.     def get_ordered_partition_list(self):
  314.         parts = self._partitions.keys()
  315.         parts.sort()
  316.         partlist = []
  317.         tmppart = None
  318.         for part in parts:
  319.             if archinfo[self._arch]['extended'] and part > (4 + FREE_MINOR_FRAC_PRI): break
  320.             tmppart = self._partitions[part]
  321.             partlist.append(part)
  322.             if tmppart.is_extended():
  323.                 for part_log in parts:
  324.                     if part_log < (4 + FREE_MINOR_FRAC_LOG): continue
  325.                     partlist.append(part_log)
  326.         return partlist
  327.  
  328.     ##
  329.     # Returns partition info in a format suitable for passing to install_profile.set_partition_tables()
  330.     def get_install_profile_structure(self):
  331.         devdic = {}
  332.         for part in self._partitions:
  333.             tmppart = self._partitions[part]
  334.             devdic[part] = { 'mb': tmppart.get_mb(), 'minor': float(part), 'origminor': tmppart.get_orig_minor(), 'type': tmppart.get_type(), 'mountpoint': tmppart.get_mountpoint(), 'mountopts': tmppart.get_mountopts(), 'format': tmppart.get_format(), 'mkfsopts': tmppart.get_mkfsopts(), 'start': 0, 'end': 0, 'resized': tmppart.get_resized() }
  335.         return devdic
  336.  
  337.     ##
  338.     # Returns the minor of the extended partition, if any
  339.     def get_extended_partition(self):
  340.         for part in self._partitions:
  341.             tmppart = self._partitions[part]
  342.             if tmppart.is_extended():
  343.                 return part
  344.         return 0
  345.  
  346.     ##
  347.     # Returns the drive model
  348.     def get_model(self):
  349.         if self._local_device:
  350.             return self._parted_dev.model
  351.         else:
  352.             return "Generic disk"
  353.  
  354.     ##
  355.     # Sets the disklabel type
  356.     def set_disklabel(self, disklabel):
  357.         self._disklabel = disklabel
  358.  
  359.     ##
  360.     # Returns the disklabel type
  361.     def get_disklabel(self):
  362.         return self._disklabel
  363.  
  364.     ##
  365.     # Returns the number of sectors on the device
  366.     def get_num_sectors(self):
  367.         return long(self._total_sectors)
  368.  
  369.     ##
  370.     # Returns the size of a cylinder in bytes
  371.     def get_cylinder_size(self):
  372.         return long(self._cylinder_bytes)
  373.  
  374.     ##
  375.     # Returns the size of a sector in bytes
  376.     def get_sector_size(self):
  377.         return long(self._sector_bytes)
  378.  
  379.     ##
  380.     # Returns the number of cylinders
  381.     def get_num_cylinders(self):
  382.         return long(self._geometry['cylinders'])
  383.  
  384.     ##
  385.     # Returns the total number of bytes on the device
  386.     def get_drive_bytes(self):
  387.         return long(self._total_bytes)
  388.  
  389.     ##
  390.     # Returns the total number of MB on the device
  391.     def get_total_mb(self):
  392.         if self._local_device:
  393.             return self._total_mb
  394.         else:
  395.             total_mb = 0
  396.             for tmppart in self._partitions:
  397.                 total_mb += self._partitions[tmppart].get_mb()
  398.             return total_mb
  399.  
  400.     ##
  401.     # Returns partition info dictionary
  402.     def get_partitions(self):
  403.         return self._partitions
  404.  
  405.     ##
  406.     # Prints disk geometry to STDOUT (no longer used)
  407.     def print_geometry(self):
  408.         print self._total_bytes, self._geometry
  409.  
  410. ##
  411. # This class represents a partition within a GLIStorageDevice object
  412. class Partition:
  413.     "Class representing a single partition within a Device object"
  414.  
  415.     _device = None
  416.     _minor = 0
  417.     _orig_minor = 0
  418.     _start = 0
  419.     _end = 0
  420.     _type = None
  421.     _mountpoint = None
  422.     _mountopts = None
  423.     _format = None
  424.     _resizeable = None
  425.     _min_mb_for_resize = 0
  426.     _mb = ""
  427.     _mkfsopts = None
  428.     _resized = False
  429.     
  430.     ##
  431.     # Initialization function for the Partition class
  432.     # @param device Parent GLIStorageDevice object
  433.     # @param minor Minor of partition
  434.     # @param mb Parameter Size of partition in MB
  435.     # @param start Parameter Start sector of partition
  436.     # @param end Parameter Start sector of partition
  437.     # @param type Parameter Type of partition (ext2, ext3, fat32, linux-swap, free, extended, etc.)
  438.     # @param mountpoint='' Mountpoint of partition
  439.     # @param mountopts='' Mount options of partition
  440.     # @param mkfsopts='' Additional mkfs options
  441.     # @param format=True Format partition
  442.     # @param existing=False This partition exists on disk
  443.     def __init__(self, device, minor, mb, start, end, type, mountpoint='', mountopts='', format=True, existing=False, origminor=0, mkfsopts='', resized=False):
  444.         self._device = device
  445.         self._minor = float(minor)
  446.         self._start = long(start)
  447.         self._end = long(end)
  448.         self._type = type or "unknown"
  449.         self._mountpoint = mountpoint
  450.         self._mountopts = mountopts
  451.         self._format = format
  452.         self._mb = mb
  453.         self._orig_minor = origminor
  454.         self._mkfsopts = mkfsopts
  455.         self._resizeable = False
  456.         self._resized = resized
  457.         if type != "free":
  458.             if existing and not origminor:
  459.                 self._orig_minor = self._minor
  460.             self._minor = int(self._minor)
  461.             self._orig_minor = int(self._orig_minor)
  462.         if existing:
  463.             try:
  464.                 parted_part = device._parted_disk.get_partition(self._orig_minor)
  465.                 label_type = device._parted_disk.type.name
  466.                 if label_type == "loop":
  467.                     dev_node = device._device
  468.                 else:
  469.                     dev_node = device._device + str(self._orig_minor)
  470. #                print "dev_node = " + dev_node
  471.                 if type == "ntfs":
  472.                     min_bytes = long(commands.getoutput("ntfsresize -f --info " + dev_node + " | grep -e '^You might resize' | sed -e 's/You might resize at //' -e 's/ bytes or .\+//'"))
  473.                     self._min_mb_for_resize = long(min_bytes / MEGABYTE) + 1
  474.                     self._resizeable = True
  475.                 elif type == "ext2" or type == "ext3":
  476.                     block_size = long(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Block size:' | sed -e 's/^Block size:\s\+//'")))
  477.                     free_blocks = long(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Free blocks:' | sed -e 's/^Free blocks:\s\+//'")))
  478.                     free_bytes = long(block_size * free_blocks)
  479.                     # can't hurt to pad (the +50) it a bit since this is really just a guess
  480.                     self._min_mb_for_resize = self._mb - long(free_bytes / MEGABYTE) + 50
  481.                     self._resizeable = True
  482.                 else:
  483.                     parted_part = self._device._parted_disk.get_partition(int(self._orig_minor))
  484.                     parted_fs = parted_part.geom.file_system_open()
  485.                     resize_constraint = parted_fs.get_resize_constraint()
  486.                     min_bytes = resize_constraint.min_size * self._device._sector_bytes
  487.                     self._min_mb_for_resize = long(min_bytes / MEGABYTE) + 1
  488.                     self._resizeable = True
  489.             except:
  490.                 self._resizeable = False
  491.  
  492.     ##
  493.     # Returns whether or not the partition is extended
  494.     def is_extended(self):
  495.         if self._type == "extended":
  496.             return True
  497.         else:
  498.             return False
  499.  
  500.     ##
  501.     # Returns whether or not the partition is logical
  502.     def is_logical(self):
  503.         if self._type == "free":
  504.             if int(self._minor) + FREE_MINOR_FRAC_LOG == self._minor:
  505.                 return True
  506.             else:
  507.                 return False
  508.         elif archinfo[self._device._arch]['extended'] and self._minor > 4:
  509.             return True
  510.         else:
  511.             return False
  512.  
  513.     ##
  514.     # Returns a list of logical partitions if this is an extended partition
  515.     def get_logicals(self):
  516.         if not self.is_extended():
  517.             return None
  518.         logicals = []
  519.         parts = self._device._partitions.keys()
  520.         parts.sort()
  521.         for part in parts:
  522.             if part < 5: continue
  523.             logicals.append(part)
  524.         logicals.sort()
  525.         return logicals
  526.  
  527.     ##
  528.     # Returns the extened parent partition if this is a logical partition (no longer used)
  529.     def get_extended_parent(self):
  530.         if not self.is_logical():
  531.             return None
  532.         else:
  533.             return self._device.get_partition_at(self._start, ignore_extended=0)
  534.  
  535.     ##
  536.     # Sets the options passed to mkfs
  537.     # @param mkfsopts Options passed to mkfs
  538.     def set_mkfsopts(self, mkfsopts):
  539.         self._mkfsopts = mkfsopts
  540.  
  541.     ##
  542.     # Returns the options passes to mkfs
  543.     def get_mkfsopts(self):
  544.         return self._mkfsopts
  545.  
  546.     ##
  547.     # Sets the start sector for the partition
  548.     # @param start Start sector
  549.     def set_start(self, start):
  550.         self._start = long(start)
  551.  
  552.     ##
  553.     # Returns the start sector for the partition
  554.     def get_start(self):
  555.         return long(self._start)
  556.  
  557.     ##
  558.     # Sets the end sector of the partition
  559.     # @param end End sector
  560.     def set_end(self, end):
  561.         self._end = long(end)
  562.  
  563.     ##
  564.     # Returns end sector for the partition
  565.     def get_end(self):
  566.         return long(self._end)
  567.  
  568.     ##
  569.     # Returns size of partition in MB
  570.     def get_mb(self):
  571.         return self._mb
  572.  
  573.     ##
  574.     # Sets size of partition in MB
  575.     # @param mb Parameter description
  576.     def set_mb(self, mb):
  577.         self._mb = mb
  578.  
  579.     ##
  580.     # Sets type of partition
  581.     # @param type Parameter description
  582.     def set_type(self, type):
  583.         self._type = type
  584.  
  585.     ##
  586.     # Returns type of partition
  587.     def get_type(self):
  588.         return self._type
  589.  
  590.     ##
  591.     # Returns parent GLIStorageDevice object
  592.     def get_device(self):
  593.         return self._device
  594.  
  595.     ##
  596.     # Sets minor of partition
  597.     # @param minor New minor
  598.     def set_minor(self, minor):
  599.         self._minor = float(minor)
  600.  
  601.     ##
  602.     # Returns minor of partition
  603.     def get_minor(self):
  604.         return float(self._minor)
  605.  
  606.     ##
  607.     # Sets the original minor of the partition
  608.     # @param orig_minor Parameter description
  609.     def set_orig_minor(self, orig_minor):
  610.         self._orig_minor = int(orig_minor)
  611.  
  612.     ##
  613.     # Returns the original minor of the partition
  614.     def get_orig_minor(self):
  615.         return self._orig_minor
  616.  
  617.     ##
  618.     # Sets the mountpoint for the partition
  619.     # @param mountpoint Mountpoint
  620.     def set_mountpoint(self, mountpoint):
  621.         self._mountpoint = mountpoint
  622.  
  623.     ##
  624.     # Returns the mountpoint for the partition
  625.     def get_mountpoint(self):
  626.         return self._mountpoint
  627.  
  628.     ##
  629.     # Sets the mount options for the partition
  630.     # @param mountopts Mount options
  631.     def set_mountopts(self, mountopts):
  632.         self._mountopts = mountopts
  633.  
  634.     ##
  635.     # Returns the mount options for the partition
  636.     def get_mountopts(self):
  637.         return self._mountopts
  638.  
  639.     ##
  640.     # Set whether to format the partition
  641.     # @param format Format this partition (True/False)
  642.     def set_format(self, format):
  643.         self._format = format
  644.  
  645.     ##
  646.     # Returns whether to format the partition
  647.     def get_format(self):
  648.         return self._format
  649.  
  650.     ##
  651.     # Returns whether to partition is resized
  652.     def get_resized(self):
  653.         return self._resized
  654.  
  655.     ##
  656.     # Returns whether the partition is resizeable
  657.     def is_resizeable(self):
  658.         return self._resizeable
  659.  
  660.     ##
  661.     # Returns minimum MB for resize
  662.     def get_min_mb_for_resize(self):
  663. #        if self.is_extended():
  664. #            min_size = self._start
  665. #            for part in self._device._partitions:
  666. #                if part < 5: continue
  667. #                if part.get_end > min_size: min_size = part.get_end()
  668. #            return min_size
  669. #        else:
  670.         if self._resizeable:
  671.             return self._min_mb_for_resize
  672.         else:
  673.             return -1
  674.  
  675.     ##
  676.     # Returns maximum MB for resize
  677.     def get_max_mb_for_resize(self):
  678.         if self._resizeable:
  679.             free_minor = 0
  680.             if self.is_logical():
  681.                 free_minor = self._minor + FREE_MINOR_FRAC_LOG
  682.             else:
  683.                 free_minor = self._minor + FREE_MINOR_FRAC_PRI
  684.             if free_minor in self._device._partitions:
  685.                 return self._mb + self._device._partitions[free_minor]._mb
  686.             else:
  687.                 return self._mb
  688.         else:
  689.             return -1
  690.  
  691.     ##
  692.     # Resizes the partition
  693.     # @param mb New size in MB
  694.     def resize(self, mb):
  695.         free_minor = self._minor
  696.         if self.is_logical():
  697.             free_minor += FREE_MINOR_FRAC_LOG
  698.         else:
  699.             free_minor += FREE_MINOR_FRAC_PRI
  700.         if mb < self._mb:
  701.             # Shrinking
  702.             if not free_minor in self._device._partitions:
  703.                 self._device._partitions[free_minor] = Partition(self._device, free_minor, 0, 0, 0, "free", format=False, existing=False)
  704.             self._device._partitions[free_minor]._mb += self._mb - mb
  705.             self._mb = mb
  706.         elif mb == self._mb + self._device._partitions[free_minor]._mb:
  707.             # Using all available unallocated space
  708.             del self._device._partitions[free_minor]
  709.             self._mb = mb
  710.         elif mb > self._mb:
  711.             # Growing
  712.             self._device._partitions[free_minor]._mb = mb - self._mb
  713.             self._mb = mb
  714.         self._resized = True
  715.  
  716. ##
  717. # Returns a list of detected partitionable devices
  718. def detect_devices():
  719.     devices = []
  720.     
  721.     # Make sure sysfs exists
  722.     # TODO: rewrite for 2.4 support
  723.     if not os.path.exists("/sys/bus"):
  724.         raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "no sysfs found (you MUST use a kernel >2.6)")
  725.     # Make sure /proc/partitions exists
  726.     if not os.path.exists("/proc/partitions"):
  727.         raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "/proc/partitions does not exist! Please make sure procfs is in your kernel and mounted!")
  728.     
  729.     # Load /proc/partitions into the variable 'partitions'
  730.     partitions = []
  731.     for line in open("/proc/partitions"):
  732.         if len(line.split()) < 4 or not line.split()[0].isdigit() or not line.split()[1].isdigit():
  733.             continue
  734.         
  735.         # Get the major, minor and device name
  736.         major = line.split()[0]
  737.         minor = line.split()[1]
  738.         device = "/dev/" + line.split()[3]
  739.         
  740.         if not major.isdigit() or not minor.isdigit():
  741.             continue
  742.             
  743.         major = int(major)
  744.         minor = int(minor)
  745.  
  746.         # If there is no /dev/'device_name', then scan
  747.         # all the devices in /dev to try and find a
  748.         # devices with the same major and minor        
  749.         if not os.path.exists(device):
  750.             device = None
  751.             for path, dirs, files in os.walk("/dev"):
  752.                 for d_file in files:
  753.                     full_file = os.path.join(path, d_file)
  754.                     if not os.path.exists(full_file):
  755.                         continue
  756.                     statres = os.stat(full_file)
  757.                     fmaj = os.major(statres.st_rdev)
  758.                     fmin = os.minor(statres.st_rdev)
  759.                     if fmaj == major and fmin == minor:
  760.                         device = full_file
  761.                         break
  762.             if not device:
  763.                 continue
  764.             
  765.         partitions.append(( major, minor, device ))
  766.     
  767.     # Scan sysfs for the devices of type 'x'
  768.     # 'x' being a member of the list below:
  769.     # TODO: grimmlin: fix for kernel 2.6
  770.         for dev_glob in ("/sys/bus/ide/devices/*/block*/dev", "/sys/bus/scsi/devices/*/block*/dev", "/sys/block/cciss*/dev", "/sys/block/ida*/dev"):
  771.                 sysfs_devices = glob(dev_glob)
  772.                 if not sysfs_devices: continue
  773.                 for sysfs_device in sysfs_devices:
  774.                         # Get the major and minor info
  775.                         try:
  776.                                 major, minor = open(sysfs_device).read().split(":")
  777.                                 major = int(major)
  778.                                 minor = int(minor)
  779.                         except:
  780.                                 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "invalid major/minor in " + sysfs_device)
  781.                         # Find a device listed in /proc/partitions
  782.                         # that has the same minor and major as our
  783.                         # current block device.
  784.                         for record in partitions:
  785.                                 if major == record[0] and minor == record[1]:
  786.                                         devices.append(record[2])
  787.  
  788.  
  789.     # For testing the partitioning code
  790.     if GLIUtility.is_file("/tmp/disk.img"):
  791.         devices.append("/tmp/disk.img")
  792.  
  793.     # We have assembled the list of devices, so return it
  794.     return devices
  795.